package im.vinci.server.syncdb.controller;

import com.aliyun.openservices.ons.api.Action;
import com.aliyun.openservices.ons.api.Consumer;
import com.aliyun.openservices.ons.api.ONSFactory;
import com.aliyun.openservices.ons.api.PropertyKeyConst;
import com.google.common.collect.ImmutableMap;
import im.vinci.server.common.push.PushService;
import im.vinci.server.config.UserProfile;
import im.vinci.server.security.ApiSecurityLabel;
import im.vinci.server.syncdb.domain.ClientUserData;
import im.vinci.server.syncdb.domain.wrapper.DownloadUserDataResponse;
import im.vinci.server.syncdb.domain.wrapper.UploadUserDataRequest;
import im.vinci.server.syncdb.service.UserDataSyncService;
import im.vinci.server.syncdb.service.UserFavoriteMusicOperateService;
import im.vinci.server.utils.JsonUtils;
import im.vinci.server.utils.UserContext;
import im.vinci.server.utils.apiresp.ResultObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Profile;
import org.springframework.core.env.Environment;
import org.springframework.web.bind.annotation.*;

import java.util.List;
import java.util.Map;
import java.util.Properties;

/**
 * Created by tim@vinci on 16/7/27.
 */
@RestController
@RequestMapping(value = "/vinci/user/syncdb", produces = "application/json;charset=UTF-8")
@Profile({UserProfile.INTG, UserProfile.QACI, UserProfile.PROD})
public class UserSyncDBController {

    private Logger logger = LoggerFactory.getLogger(getClass());

    @Autowired
    private Environment env;

    @Autowired
    private UserDataSyncService userDataSyncService;

    @Autowired
    private UserFavoriteMusicOperateService userFavoriteMusicOperateService;

    @Autowired
    private PushService pushService;

    @Value("${ons.user_client_data_sync.topic}")
    private String userSyncDataOnsTopic;

    @Value("${ons.user_client_data_sync.sync_push_msg.consumeId}")
    private String userSyncDataPushMsgConsumerId;

    @Value("${ons.user_client_data_sync.user_collect.consumeId}")
    private String userSyncDataUserCollectConsumeId;

    @Bean(destroyMethod = "shutdown")
    @Profile({"qaci","prod"})
    public Consumer newUserDataSyncReceiveConsumer() {
        Properties properties = new Properties();

        properties.put(PropertyKeyConst.AccessKey, env.getRequiredProperty("ons.accessKey"));
        properties.put(PropertyKeyConst.SecretKey, env.getRequiredProperty("ons.secretKey"));

        properties.put(PropertyKeyConst.ConsumerId, userSyncDataPushMsgConsumerId);
        Consumer consumer = ONSFactory.createConsumer(properties);
        _runUserDataSyncReceiveConsumer(consumer);
        return consumer;
    }

    @Bean(destroyMethod = "shutdown")
    @Profile({"qaci","prod"})
    public Consumer newUserFavoriteMusicConsumer() {
        Properties properties = new Properties();

        properties.put(PropertyKeyConst.AccessKey, env.getRequiredProperty("ons.accessKey"));
        properties.put(PropertyKeyConst.SecretKey, env.getRequiredProperty("ons.secretKey"));

        properties.put(PropertyKeyConst.ConsumerId, userSyncDataUserCollectConsumeId);
        Consumer consumer = ONSFactory.createConsumer(properties);
        _runUserFavoriteMusicConsumer(consumer);
        return consumer;

    }

    /**
     * 收取所有数据库更新消息,收到后发送给前端设备push message
     */
    private void _runUserDataSyncReceiveConsumer(Consumer consumer) {
        consumer.subscribe(userSyncDataOnsTopic, "user_db_sync", (msg, context) -> {
            logger.info("receive user data sync msg {}, to push msg to device client:{}", msg.getMsgID(), msg.getKey());
            try {
                long uid = Long.parseLong(msg.getUserProperties("uid"));
                long cv = Long.parseLong(msg.getUserProperties("update_version"));
                String table = msg.getUserProperties("table_name");
                boolean result = pushService.pushMessageByUidAsync(
                        uid,
                        ImmutableMap.of("action", "userdata.sync"),
                        JsonUtils.encode(ImmutableMap.of(
                                "uid", uid,
                                "table", table,
                                "max_update_version", cv
                        ))
                );

                if (result) {
                    return Action.CommitMessage;
                }
                return Action.ReconsumeLater;
            } catch (NumberFormatException e) {
                return Action.CommitMessage;
            } catch (Exception e) {
                logger.error("occurred unexpected error:", e);
                return Action.ReconsumeLater;
            }
        });
        consumer.start();

    }

    private void _runUserFavoriteMusicConsumer(Consumer consumer) {
        consumer.subscribe(userSyncDataOnsTopic, "user_db_sync", (msg, context) -> {
            logger.info("receive user data sync msg {}, to update favorite musics:{}", msg.getMsgID(), msg.getKey());
            try {
                long userid = Long.parseLong(msg.getUserProperties("uid"));
                long version = Long.parseLong(msg.getUserProperties("update_version"));
                String tableName = msg.getUserProperties("table_name");

                logger.info("update favorite music data userid: {}, tableName: {}, version: {}", userid, tableName, version);

                if (!tableName.equals("FAVORITE_MUSIC")) {
                    return Action.CommitMessage;
                }

                List<ClientUserData> datas = userDataSyncService.getUserData(userid, tableName, version);

                if (datas == null || datas.isEmpty()) {
                    return Action.CommitMessage;
                }

                logger.info("update favorite music data userid: {}, tableName: {}, version: {}", userid, tableName, version);

                boolean result = userFavoriteMusicOperateService.updateData(datas);

                logger.info("update favorite music data OK");

                if (result) {
                    return Action.CommitMessage;
                }
                return Action.ReconsumeLater;
            } catch (NumberFormatException e) {
                return Action.CommitMessage;
            } catch (Exception e) {
                logger.error("runUserFavoriteMusicConsumer error:", e);
                return Action.ReconsumeLater;
            }
        });
        consumer.start();

    }

    @RequestMapping(value = "/download", method = RequestMethod.POST)
    @ApiSecurityLabel(isCheckLogin = true)
    public ResultObject<DownloadUserDataResponse> downloadUserData(@RequestParam("table") String table, @RequestParam("max_update_version") long version) {
        return new ResultObject<>(userDataSyncService.downloadUserData(UserContext.getUserInfo().getId(), table, version));
    }

    @RequestMapping(value = "/upload", method = RequestMethod.POST)
    @ApiSecurityLabel(isCheckLogin = true)
    public ResultObject<Map<String, Long>> uploadUserData(final @RequestBody UploadUserDataRequest request) {
        long cv = userDataSyncService.plusUserDataUpdateVersion(UserContext.getUserInfo().getId(), request.getTable());

        boolean isCommit =
                userDataSyncService.uploadUserData(UserContext.getUserInfo().getId(), request.getTable(), cv, request.getRecords());
        return new ResultObject<>(ImmutableMap.of("update_version", cv));
    }

}
